//
//  Texture.cpp
//  Ray Tracer
//
//  Created by Kaila, Ashish on 3/23/13.
//  Copyright (c) 2013 Kaila, Ashish. All rights reserved.
//

#include "TextureMaterial.h"
#include "../Utils/bmp_io.h"
#include "ITextureMapper.h"
#include "../Shapes/scene_object.h"

TextureMaterial::TextureMaterial(char* fileName,
                                 Colour shadowAmbientColour,
                                 double specularExp,
                                 double diffuseReflectivityFactor,
                                 double specularReflectivityFactor,
                                 double refractiveIndex)
: Material(specularExp, diffuseReflectivityFactor, specularReflectivityFactor, refractiveIndex),
  _shadowAmbientColour(shadowAmbientColour),
  width(0),
  height(0),
  r(NULL),
  g(NULL),
  b(NULL)
{    
    unsigned char* rarray;
    unsigned char* garray;
    unsigned char* barray;
    
    if (bmp_read (fileName,
                  &width,
                  &height,
                  &rarray,
                  &garray,
                  &barray))
    {
        throw "Cannot load texture";
    }

#ifdef __APPLE__
    r.reset(rarray);
    g.reset(garray);
    b.reset(barray);
#else
    r = rarray;
    g = garray;
    b = barray;
#endif
}

TextureMaterial::~TextureMaterial()
{
#ifndef __APPLE__
    delete[] r;
    delete[] g;
    delete[] b;
#endif
}

unsigned long TextureMaterial::getWidth() const
{
    return width;
}

unsigned long TextureMaterial::getHeight() const
{
    return height;
}

// Returns a clamped colour at a specified location
Colour TextureMaterial::getColour(int i, int j) const
{
    if ((i < 0) || (i > width) || (j < 0) || (j > height))
    {
        throw "Out of range";
    }
    
    unsigned long index = i + j * width;
    
#ifdef __APPLE__
    Colour col = Colour((double)(r.get()[index]) / (double)255.0, (double)(g.get()[index]) / (double)255.0, (double)(b.get()[index]) / (double)255.0);
#else
    Colour col = Colour((double)(r[index]) / (double)255.0, (double)(g[index]) / (double)255.0, (double)(b[index]) / (double)255.0);
#endif
    col.clamp();
    
    return col;
}

Colour TextureMaterial::getColour(ColourType type, const Intersection& intersection, bool inShadow) const
{
    double a = 0;
    double b = 0;
    
    const ITextureMapper& textureMapper = intersection.obj->getTextureMapper();
    
    if (!textureMapper.mapTexture(intersection, a, b))
    {
        return Colour();
    }
    
    int i = (int)abs((a * (int)width) - 0.5);
    int j = (int)abs((b * (int)height) - 0.5);
    
    double dx = fabs(a * width - 0.5) - i;
    double dy = fabs(b * height - 0.5) - j;
    
    // Clamp values
    i = fmin(fmax(i, 0), width - 1);
    int iNext = fmin(fmax(i + 1, 0), width - 1);
    
    j = fmin(fmax(j, 0), height - 1);
    int jNext = fmin(fmax(j + 1, 0), height - 1);
    
    // Perform bi-linear interpolation
    Colour col = (1 - dx) * (1 - dy) * getColour(i, j);
    col = col + dx * (1 - dy) * getColour(iNext, j);
    col = col + (1 - dx) * dy * getColour(i, jNext);
    col = col + dx * dy * getColour(iNext, jNext);
    
    if (inShadow)
    {
        return Colour(_shadowAmbientColour[0] * col[0], _shadowAmbientColour[1] * col[1], _shadowAmbientColour[2] * col[2]);
    }
    
    return col;
}